{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Http Response\n",
    "\n",
    "## Usage\n",
    "\n",
    "To construct a response packet you have a variety of facilities available.\n",
    "\n",
    "Previously we saw how to parse HTTP responses using `HttpParser`.  Of-course, we can also construct a response packet using `HttpParser` class."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "b'HTTP/1.1 200 OK\\r\\nContent-Length: 0\\r\\n\\r\\n'\n"
     ]
    }
   ],
   "source": [
    "from proxy.http.parser import HttpParser, httpParserTypes\n",
    "from proxy.common.constants import HTTP_1_1\n",
    "\n",
    "response = HttpParser(httpParserTypes.RESPONSE_PARSER)\n",
    "response.code = b'200'\n",
    "response.reason = b'OK'\n",
    "response.version = HTTP_1_1\n",
    "\n",
    "print(response.build_response())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "But, this is a painful way to construct responses.  Hence, other high level abstractions are available.\n",
    "\n",
    "Example, following one liner will give us the same response packet."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "b'HTTP/1.1 200 OK\\r\\nContent-Length: 0\\r\\n\\r\\n'\n"
     ]
    }
   ],
   "source": [
    "from proxy.http.responses import okResponse\n",
    "\n",
    "print(okResponse().tobytes())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Notice how `okResponse` will always add a `Content-Length` header for you.\n",
    "\n",
    "You can also customize other headers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "b'HTTP/1.1 200 OK\\r\\nX-Custom-Header: my value\\r\\nContent-Length: 0\\r\\n\\r\\n'\n"
     ]
    }
   ],
   "source": [
    "response = okResponse(\n",
    "    headers={\n",
    "        b'X-Custom-Header': b'my value',\n",
    "    },\n",
    ")\n",
    "\n",
    "print(response.tobytes())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's add some content to our response packet"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "b'HTTP/1.1 200 OK\\r\\nX-Custom-Header: my value\\r\\nContent-Length: 11\\r\\n\\r\\nHello World'\n"
     ]
    }
   ],
   "source": [
    "response = okResponse(\n",
    "    content=b'Hello World',\n",
    "    headers={\n",
    "        b'X-Custom-Header': b'my value',\n",
    "    },\n",
    ")\n",
    "\n",
    "print(response.tobytes())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note, how `okResponse` automatically added a `Content-Length` header for us.\n",
    "\n",
    "Depending upon `--min-compression-length` flag, `okResponse` will also perform compression for content.\n",
    "\n",
    "Example, default value for min compression length is 20."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "b\"HTTP/1.1 200 OK\\r\\nX-Custom-Header: my value\\r\\nContent-Encoding: gzip\\r\\nContent-Length: 23\\r\\n\\r\\n\\x1f\\x8b\\x08\\x00\\x80'\\xf4a\\x02\\xff\\xf3\\xf0\\xc0\\x02\\x00h\\x81?s\\x15\\x00\\x00\\x00\"\n"
     ]
    }
   ],
   "source": [
    "response = okResponse(\n",
    "    content=b'H' * 21,\n",
    "    headers={\n",
    "        b'X-Custom-Header': b'my value',\n",
    "    },\n",
    ")\n",
    "\n",
    "print(response.tobytes())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can pass a custom value for `min_compression_length` kwarg to `okResponse`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "b'HTTP/1.1 200 OK\\r\\nHost: jaxl.com\\r\\nContent-Length: 21\\r\\n\\r\\nHHHHHHHHHHHHHHHHHHHHH'\n"
     ]
    }
   ],
   "source": [
    "response = okResponse(\n",
    "    content=b'H' * 21,\n",
    "    headers={\n",
    "        b'Host': b'jaxl.com',\n",
    "    },\n",
    "    min_compression_length=21,\n",
    ")\n",
    "\n",
    "print(response.tobytes())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Internally, `okResponse` uses `build_http_response` and hence you can also pass any argument also accepted by `build_http_response`.  Example, it supports a `conn_close` argument which will add a `Connection: close` header.  Simply, pass `conn_close=True`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "b'HTTP/1.1 200 OK\\r\\nHost: jaxl.com\\r\\nContent-Length: 11\\r\\nConnection: close\\r\\n\\r\\nHello World'\n"
     ]
    }
   ],
   "source": [
    "response = okResponse(\n",
    "    content=b'Hello World',\n",
    "    headers={\n",
    "        b'Host': b'jaxl.com',\n",
    "    },\n",
    "    conn_close=True,\n",
    ")\n",
    "\n",
    "print(response.tobytes())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Chunked Encoding\n",
    "\n",
    "You can also send chunked encoded responses."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "b'HTTP/1.1 200 OK\\r\\nTransfer-Encoding: chunked\\r\\n\\r\\n5\\r\\nHello\\r\\n5\\r\\n Worl\\r\\n1\\r\\nd\\r\\n0\\r\\n\\r\\n'\n"
     ]
    }
   ],
   "source": [
    "from proxy.http.parser import ChunkParser\n",
    "\n",
    "chunks = ChunkParser.to_chunks(b'Hello World', chunk_size=5)\n",
    "response = okResponse(\n",
    "    content=chunks,\n",
    "    headers={\n",
    "        b'Transfer-Encoding': b'chunked',\n",
    "    },\n",
    "    # Avoid compressing chunks for demo purposes here\n",
    "    # Ideally you should omit this flag and send\n",
    "    # compressed chunks.\n",
    "    min_compression_length=len(chunks),\n",
    ")\n",
    "\n",
    "print(response.tobytes())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we omit the `min_compression_length` flag"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "b'HTTP/1.1 200 OK\\r\\nTransfer-Encoding: chunked\\r\\nContent-Encoding: gzip\\r\\n\\r\\n\\x1f\\x8b\\x08\\x00\\xd3\\n\\xf1a\\x02\\xff3\\xe5\\xe5\\xf2H\\xcd\\xc9\\xc9\\xe7\\xe52\\xe5\\xe5R\\x08\\xcf/\\xca\\xe1\\xe52\\xe4\\xe5J\\xe1\\xe52\\xe0\\xe5\\xe2\\xe5\\x02\\x00\\x90S\\xbb/\\x1f\\x00\\x00\\x00'\n"
     ]
    }
   ],
   "source": [
    "response = okResponse(\n",
    "    content=chunks,\n",
    "    headers={\n",
    "        b'Transfer-Encoding': b'chunked',\n",
    "    },\n",
    ")\n",
    "\n",
    "print(response.tobytes())"
   ]
  }
 ],
 "metadata": {
  "interpreter": {
   "hash": "da9d6927d62b2b95bde149eedfbd5367cb7f465aad65a736f49c99ee3db39df7"
  },
  "kernelspec": {
   "display_name": "Python 3.10.0 64-bit ('venv310': venv)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.0"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
